﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.ContentManagement.MetaData;
using Orchard.Core.Routable.Models;
using Orchard.Core.Routable.Services;
using Orchard.Localization;
using Orchard.Logging;
using Orchard.Security;
using Orchard.Settings;
using Orchard.UI.Notify;
using Contrib.Chapters.Models;
using Orchard.Core.Common.Models;
using Orchard.Indexing;
using Orchard.ContentManagement.Aspects;

namespace Contrib.Chapters.Services {

    [UsedImplicitly]
    public class ChapterService : IChapterService {
        private readonly IContentManager _contentManager;
        private readonly IContentDefinitionManager _contentDefinitionManager;

        public ChapterService(
            IContentManager contentManager,
            INotifier notifier,
            IContentDefinitionManager contentDefinitionManager,
            IAuthorizationService authorizationService) {
            _contentManager = contentManager;
            _contentDefinitionManager = contentDefinitionManager;
            Logger = NullLogger.Instance;
            T = NullLocalizer.Instance;
        }

        public ILogger Logger { get; set; }
        public virtual ISite CurrentSite { get; set; }
        public virtual IUser CurrentUser { get; set; }
        public Localizer T { get; set; }

        public ChapterPart GetChapter(int id) {
            return _contentManager.Get<ChapterPart>(id);
        }

        public IEnumerable<ChapterPart> GetChapters(BookPart book) {
            return _contentManager.Query<ChapterPart, ChapterPartRecord>()
                .Where(part => part.Book == book.ContentItem.Record)
                .List()
                .OrderBy(chapter => chapter);
        }

        public ChapterPart GetChapterBySlug(string chapterSlug)
        {
            return _contentManager.Query<ChapterPart, ChapterPartRecord>()
                .Join<RoutePartRecord>().Where(rr => rr.Slug == chapterSlug)
                .List().FirstOrDefault();
        }

        public BookPart GetBook(int id) {
            return _contentManager.Get(id).As<BookPart>();
        }

        public IEnumerable<BookPart> GetBooks() {
            return _contentManager.Query<BookPart, BookPartRecord>()
                .Join<RoutePartRecord>()
                .OrderBy(part => part.Title)
                .List();
        }

        public IEnumerable<ChapterPart> GetChildren(ChapterPart chapterPart) {
            return _contentManager.Query<ChapterPart, ChapterPartRecord>()
                .Where(chapter => chapter.Book.Id == chapterPart.Id)
                .List()
                .OrderBy(c => c);
        }

        public IEnumerable<ChapterPart> GetDirectChildren(ChapterPart chapterPart) {
            return _contentManager.Query<ChapterPart, ChapterPartRecord>()
                .Join<CommonPartRecord>()
                .Where(cp => cp.Container.Id == chapterPart.Id)
                .List()
                .OrderBy(c => c);
        }

        public IEnumerable<ChapterPart> GetSiblings(ChapterPart chapterPart) {
            return _contentManager.Query<ChapterPart, ChapterPartRecord>()
                .Join<CommonPartRecord>()
                .Where(cp => cp.Container.Id == chapterPart.Container.Id)
                .List()
                .OrderBy(c => c);
        }

        public void CreateChapterContentType(BookPart book) {
            // create the associated chapter's content type
            book.ChapterTypeName = GenerateChapterTypeName(book.Name);

            _contentDefinitionManager.AlterTypeDefinition(book.ChapterTypeName,
                cfg => cfg
                    .WithSetting("BookId", book.Id.ToString())
                    .WithPart("ChapterPart")
                    .WithPart("RoutePart")
                    .WithPart("CommonPart")
                    .WithPart("BodyPart")
                    .DisplayedAs(book.Name + " Chapter")
                    .Indexed()
                );
        }

        public string GenerateChapterTypeName(string bookName) {
            var disallowed = new Regex(@"[^\w]+");
            return disallowed.Replace(bookName, String.Empty) + "Chapter";
        }

        public ChapterPart NewChapter(BookPart book) {
            var chapter = _contentManager.New<ChapterPart>(book.ChapterTypeName);
            chapter.Record.Book = book.ContentItem.Record;

            return chapter;
        }

        public void EditBook(BookPart bookPart, string oldName) {
            // rename book
            _contentDefinitionManager.AlterTypeDefinition(bookPart.ChapterTypeName,
                                                          cfg => cfg.DisplayedAs(bookPart.Name + " Chapter"));
        }

        public void DeleteBook(BookPart book) {
            _contentManager.Remove(book.ContentItem);

            // removing chapters
            foreach(var chapter in GetChapters(book)) {
                _contentManager.Remove(chapter.ContentItem);    
            }

            // todo: delete dynamic content type
        }

        public void ChangeName(ChapterPart chapterPart, string oldName) {
            if(String.Equals(chapterPart.Title, oldName, StringComparison.OrdinalIgnoreCase))
                return;

            // rename chapter definition
            _contentDefinitionManager.AlterTypeDefinition(GenerateChapterTypeName(oldName),
                cfg => cfg.Named(GenerateChapterTypeName(chapterPart.Title)));
        }

        public void Delete(ChapterPart chapter) {
            var children = GetChildren(chapter);

            foreach (var child in children) {
                _contentManager.Remove(child.ContentItem);
            }

            _contentManager.Remove(chapter.ContentItem);
        }

        public BookPart GetBookByName(string name) {
            if (String.IsNullOrWhiteSpace(name)) {
                throw new ArgumentNullException("name");
            }

            return _contentManager
                .Query<BookPart>()
                .Join<RoutePartRecord>()
                .Where(r => r.Title == name)
                .List()
                .FirstOrDefault();
        }

        public void MoveChapter(BookPart book, ChapterPart chapter, ChapterPart parentChapter) {
            chapter.Container = parentChapter == null ? (IContent)book : (IContent)parentChapter;
            chapter.Path = chapter.As<IRoutableAspect>().GetPathWithSlug(chapter.Slug);
        }
    }
}
